﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;

namespace _MEMORY_QUERY_TOOL
{
    public partial class Form1 : Form
    {
        public Form1() {
            InitializeComponent();
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.StartPosition = FormStartPosition.CenterScreen;
        }

        Process pChoose;            //选择的进程
        int searchType;             //搜索类型
        int searchBytes;            //搜索字节数
        int nBaseAddr;              //基地址
        
        int[] arrLastSearchAddrs;       //保存搜索出来的地址
        long[] arrLastSearchValues;     //保存搜索出来的值
        List<int> listTempAddrs = new List<int>();      //临时地址列表
        List<long> listTempValues = new List<long>();   //你是数据列表

        Thread threadSearch;            //搜索时候的线程
        Thread threadRefresh;           //后台显示刷新列表线程

        long searchNumOne;              //要搜索的数据1
        long searchNumTwo;              //要搜索的数据2 搜索两只之间用

        int nSelectIndex = 0;           //listview的选中项索引

        private void Form1_Load(object sender, EventArgs e) {   //窗体初始化
            comboBox2.Items.Add("1字节");
            comboBox2.Items.Add("2字节");
            comboBox2.Items.Add("4字节");
            comboBox2.Items.Add("8字节");
            comboBox2.SelectedIndex = 2;
            comboBox1.Items.Clear();
            comboBox1.Items.Add("精确值");
            comboBox1.Items.Add("大于此值");
            comboBox1.Items.Add("小于此值");
            comboBox1.Items.Add("两者之间");
            comboBox1.SelectedIndex = 0;
            groupBox1.Enabled = false;
            groupBox2.Enabled = false;
            //注意    界面上看起来像只有一个textbox
            tbx_searchval_one.Width = tbx_searchval_two.Right - tbx_searchval_one.Left;
            panel1.Hide();          //panel用于添加描述信息
        }

        private void btn_open_Click(object sender, EventArgs e) {   //打开进程
            FrmOpenProcess frmProcess = new FrmOpenProcess();
            if (frmProcess.ShowDialog() == DialogResult.OK) {
                label5.Text = "当前进程: " + frmProcess.P.ProcessName;
                this.Text = "内存修改器 - " + frmProcess.P.ProcessName;
                pChoose = frmProcess.P;
                listView2.Items.Clear();    //清楚listview2
                ReSetForm();                //重置窗体
            }
        }

        private void btn_first_Click(object sender, EventArgs e) {
            if (btn_first.Text == "撤销搜索") {
                ReSetForm();
                return;
            }
            if (pChoose.HasExited) {
                MessageBox.Show("未发现进程 进程可能已经结束");
                return;
            }
            try {
                searchNumOne = Convert.ToInt64(tbx_searchval_one.Text);
                searchNumTwo = Convert.ToInt64(tbx_searchval_two.Text);
                searchType = comboBox1.SelectedIndex;           //设置搜索类型
                searchBytes = 1 << comboBox2.SelectedIndex;     //设置搜索的字节数
                //启动线程开始搜索
                threadSearch = new Thread(new ThreadStart(FirstSearchThread));
                threadSearch.IsBackground = true;
                threadSearch.Start();
                //搜索期间禁用一些控件 (效率没有CE那么快) 
                EnableControls(false);
            } catch (Exception ex) {
                MessageBox.Show(ex.Message);
            }

        }
        //首次搜索
        public void FirstSearchThread() {
            Win32.MEMORY_BASIC_INFORMATION stMBI = new Win32.MEMORY_BASIC_INFORMATION();
            int searchLen = Marshal.SizeOf(stMBI);
            nBaseAddr = 0x000000;
            int nReadSize = 0;      //实际读取字节数
            while (nBaseAddr >= 0 && nBaseAddr <= 0x7fffffff && stMBI.RegionSize >= 0) {    //nBaseAddr >= 0 在这期间nBaseAddr 可能溢出int范围变成负数
                progressBar1.Invoke(new MethodInvoker(() => {                       //循环一次 重置一下进度条 好吧承认影响效率 不过内存块扫描速度比较快
                    progressBar1.Value = (int)((double)nBaseAddr / 0x7FFFFFFF * 100);
                }));
                searchLen = Win32.VirtualQueryEx(pChoose.Handle, (IntPtr)nBaseAddr, out stMBI, Marshal.SizeOf(stMBI));  //扫描内存信息 
                if (searchLen == Marshal.SizeOf(typeof(Win32.MEMORY_BASIC_INFORMATION))) {
                    if (stMBI.State == Win32.MEM_COMMIT && stMBI.Protect == Win32.PAGE_READWRITE) {     //如果是已物理分配 并且是 可读写内存 那么读取内存
                        byte[] byData = new byte[stMBI.RegionSize];
                        if (Win32.ReadProcessMemory(pChoose.Handle, (IntPtr)nBaseAddr, byData, stMBI.RegionSize, out nReadSize))
                            if (nReadSize == stMBI.RegionSize)      //如果和实际读取数相符 那么搜索数据
                                SearchDataFirst(byData, searchBytes, searchNumOne, searchNumTwo);
                    }
                } else {
                    break;
                }
                nBaseAddr += stMBI.RegionSize;      //设置基地址偏移
            }
            //扫描完成 界面呈现变化
            this.BeginInvoke(new MethodInvoker(() => {
                EnableControls(true);                   //恢复禁用控件
                ShowResult();                           //显示查询结果
                btn_next.Enabled = true;                //恢复继续搜索按钮
                comboBox2.Enabled = false;              //进制更换搜索字节数
                btn_first.Text = "撤销搜索";
                comboBox1.Items.Add("未变动数据");       //增加两项
                comboBox1.Items.Add("变动的数据");
                //开始后台每秒刷新一下listview里面地址对应的新数据
                threadRefresh = new Thread(new ThreadStart(() => {
                    while (listView1.Items.Count > 0 || listView2.Items.Count > 0) {
                        try {
                            this.Invoke(new MethodInvoker(() => {
                                RefreshListView(listView1, 0, 1);
                                RefreshListView(listView2, 1, 3);
                            }));
                        } catch { }
                        Thread.Sleep(1000);
                    }
                }));
                threadRefresh.IsBackground = true;
                threadRefresh.Start();
            }));
        }
        //刷新listview的数据显示
        public void RefreshListView(ListView lstv, int addrIndex, int valueIndex) {
            int num = 0;
            int numBytesRead = 0;
            byte[] byRead = new byte[searchBytes];
            for (int i = 0, loopLen = lstv.Items.Count; i < loopLen; i++) {
                //对listview里面的每个地址重新读取一下新数据并显示
                if (Win32.ReadProcessMemory(pChoose.Handle, (IntPtr)Convert.ToInt32(lstv.Items[i].SubItems[addrIndex].Text,16), byRead, searchBytes, out numBytesRead)) {
                    if (numBytesRead == searchBytes) {
                        num = byRead[searchBytes - 1];
                        for (int j = searchBytes, k = 2; j > 1; j--, k++) {
                            num = num << 8;
                            num = num | byRead[searchBytes - k];
                        }
                        if (lstv.Items[i].SubItems[valueIndex].Text != num.ToString())
                            lstv.Items[i].SubItems[valueIndex].Text = num.ToString();
                        return;
                    }
                }
                lstv.Items[i].SubItems[valueIndex].Text = "??";
            }
        }
        //首次搜索中的数据比较
        public void SearchDataFirst(byte[] byData, int typeOfByte, long valueOne, long valueTwo) {
            long num = 0;       //内存中读取上来的相应字节数据的值
            for (int i = 0, len = byData.Length - typeOfByte; i < len; i++) {
                num = byData[i + typeOfByte - 1];
                for (int j = typeOfByte,k = 2; j > 1; j--,k++) {
                    num = num << 8;
                    num = num | byData[i + typeOfByte - k];
                }
                switch (searchType) {       //判断搜索类型
                    case 0:
                        if (num == valueOne)
                            AddToList(nBaseAddr + i, num);  //满足相应条件把地址和值保存起来
                        break;
                    case 1:
                        if (num > valueOne)
                            AddToList(nBaseAddr + i, num);
                        break;
                    case 2:
                        if (num < valueOne)
                            AddToList(nBaseAddr + i, num);
                        break;
                    case 3:
                        if (num >= valueOne && num <= valueTwo)
                            AddToList(nBaseAddr + i, num);
                        break;
                }
            }
        }
        //添加进临时列表
        public void AddToList(int baseAddr,long value) {
            listTempAddrs.Add(baseAddr);    //注意 貌似List最大容量只能256M
            listTempValues.Add(value);      //因为有时候数据多的时候 在这个list上 第33554433个数据就报内存溢出错误 33554432 * 8 / 1024 / 1024 = 256M
        }                                   //我的意思就是 简而言之 程序可能会出现内存溢出的情况
        //继续搜索
        private void btn_next_Click(object sender, EventArgs e) {
            try {
                if (pChoose.HasExited) {
                    MessageBox.Show("未发现进程 可能进程已结束");
                    return;
                }
                if (listView1.Items.Count <= 0) {
                    MessageBox.Show("已经没有数据可以继续筛选");
                    return;
                }
                searchNumOne = Convert.ToInt64(tbx_searchval_one.Text);
                searchNumTwo = Convert.ToInt64(tbx_searchval_two.Text);
                EnableControls(false);                  //禁用控件
                progressBar1.Value = 0;                 //重置进度条
                searchType = comboBox1.SelectedIndex;   //重新获取搜索的类型
                //开启线程进行第二次搜索
                threadSearch = new Thread(new ThreadStart(NextSearchThread));
                threadSearch.IsBackground = true;
                threadSearch.Start();
            } catch (Exception ex) {
                MessageBox.Show(ex.Message);
            }
        }

        public void NextSearchThread() {
            long num = 0;                   //读取出来的值
            byte[] byRead = new byte[searchBytes];      //相应字节数组
            int numBytesRead = 0;           //实际读取字节数
            int index = 0;                              //循环的索引
            int loopLen = arrLastSearchAddrs.Length;    //循环的长度
            //搜索同时开启线程 计算 进度条
            new Thread(new ThreadStart(() => {
                while (progressBar1.Value < 100) {
                    progressBar1.Invoke(new MethodInvoker(() => {
                        progressBar1.Value = (int)((double)index / loopLen * 100);
                    }));
                    Thread.Sleep(100);      //如果进度条没满 没100 毫秒重置一次
                }
            })).Start();
            //开始在上次搜索数据中 进行符合条件的筛选
            for (index = 0, loopLen = arrLastSearchAddrs.Length; index < loopLen; index++) {
                if (Win32.ReadProcessMemory(pChoose.Handle, (IntPtr)arrLastSearchAddrs[index], byRead, searchBytes, out numBytesRead)) {
                    num = byRead[searchBytes - 1];
                    for (int j = searchBytes, k = 2; j > 1; j--, k++) {
                        num = num << 8;
                        num = num | byRead[searchBytes - k];
                    }
                    switch (searchType) {
                        case 0:
                            if (num == searchNumOne)
                                AddToList(arrLastSearchAddrs[index], num);
                            break;
                        case 1:
                            if (num > searchNumOne)
                                AddToList(arrLastSearchAddrs[index], num);
                            break;
                        case 2:
                            if (num < searchNumOne)
                                AddToList(arrLastSearchAddrs[index], num);
                            break;
                        case 3:
                            if (num >= searchNumOne && num <= searchNumTwo)
                                AddToList(arrLastSearchAddrs[index], num);
                            break;
                        case 4:
                            if (num == arrLastSearchValues[index])
                                AddToList(arrLastSearchAddrs[index], num);
                            break;
                        case 5:
                            if (num != arrLastSearchValues[index])
                                AddToList(arrLastSearchAddrs[index], num);
                            break;
                    }
                }
            }
            //搜索完成 显示结果 已经 恢复禁用控件
            this.Invoke(new MethodInvoker(() => {
                ShowResult();
                EnableControls(true);
            }));
        }
        //显示结果
        public void ShowResult() {
            listView1.Items.Clear();
            arrLastSearchAddrs = listTempAddrs.ToArray();
            listTempAddrs.Clear();          //清楚临时列表 
            arrLastSearchValues = listTempValues.ToArray();
            listTempValues.Clear();
            int loopLen = arrLastSearchAddrs.Length > 100 ? 100 : arrLastSearchAddrs.Length;
            for (int i = 0; i < loopLen; i++) {
                ListViewItem item = new ListViewItem(arrLastSearchAddrs[i].ToString("X").PadLeft(8, '0'));
                item.SubItems.Add(arrLastSearchValues[i].ToString());
                listView1.Items.Add(item);
            }
            label1.Text = "一共找到 " + arrLastSearchAddrs.Length + " 个数据 " +
                (arrLastSearchAddrs.Length > 100 ? "列表中只显示前 100 个" : "");
            progressBar1.Value = 100;   //将进度条设置满
            GC.Collect();
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
            if (comboBox1.SelectedIndex == 3)       //如果是搜索 "两者之间" 那么 显示两个textbox (一直是两个 只是叠在一起)
                tbx_searchval_one.Width = tbx_searchval_two.Width;
            else
                tbx_searchval_one.Width = tbx_searchval_two.Right - tbx_searchval_one.Left;
            tbx_searchval_two.Text = "0";
            tbx_searchval_one.Focus();
        }
        //当listview1的选择发生变化的时候 把相应数据显示到 "操作" 组
        private void listView1_SelectedIndexChanged(object sender, EventArgs e) {
            groupBox2.Enabled = true;       //个人觉得 "操作" 组过于风险 所以只有在listview里面选择的时候才让其可用
            if (listView1.SelectedItems.Count <= 0)
                return;
            int index = listView1.SelectedItems[0].Index;
            tbx_addr.Text = listView1.Items[index].SubItems[0].Text;
            tbx_editvalue.Text = listView1.Items[index].SubItems[1].Text;
        }
        //是手动写入地址还是 用默认地址
        private void btn_modify_Click(object sender, EventArgs e) {
            if (btn_modify.Text == "输入地址") {
                btn_modify.Text = "确认地址";
            } else {
                btn_modify.Text = "输入地址";
            }
            //为了安全 在手动输入没有确认地址的时候 其他控件全部禁用
            tbx_addr.ReadOnly =
                listView1.Enabled =
                listView2.Enabled =
                groupBox1.Enabled =
                btn_addlist.Enabled =
                btn_removelist.Enabled =
                btn_write.Enabled =
                btn_open.Enabled = !btn_open.Enabled;
        }
        //写入内存中
        private void btn_write_Click(object sender, EventArgs e) {
            int bytesSize = 1 << comboBox2.SelectedIndex;
            byte[] byWrite = new byte[bytesSize];
            long numWrite = Convert.ToInt64(tbx_editvalue.Text);
            int numAddr = Convert.ToInt32(tbx_addr.Text, 16);
            int numWriteSize = 0;
            //将数据写入byte数组中
            for (int i = 0; i < bytesSize; i++) {
                byWrite[i] = (byte)((numWrite & (0x00000000000000FF << i * 8)) >> i * 8);
            }
            if (Win32.WriteProcessMemory(pChoose.Handle, (IntPtr)numAddr, byWrite, bytesSize, out numWriteSize)) {
                if (numWriteSize == bytesSize)      //如果和实际写入字节数一样提示成功
                    MessageBox.Show("写入成功!");
            } else
                MessageBox.Show("写入失败");
        }
        //将listview1中的数据加入到listview2中
        private void btn_addlist_Click(object sender, EventArgs e) {
            if (listView1.SelectedItems.Count <= 0) {
                MessageBox.Show("没有选中项");
                return;
            }
            int index = listView1.SelectedItems[0].Index;
            listView1.SelectedItems.Clear();
            listView2.SelectedItems.Clear();
            for (int i = 0; i < listView2.Items.Count; i++) {
                if (listView1.Items[index].SubItems[0].Text == listView2.Items[i].SubItems[1].Text) {
                    MessageBox.Show("该项已添加");
                    listView2.Items[i].Selected = true;
                    listView2.Focus();
                    return;
                }
            }
            ListViewItem items = new ListViewItem("没有添加描述信息");
            items.SubItems.Add(listView1.Items[index].SubItems[0].Text);
            items.SubItems.Add((1 << comboBox2.SelectedIndex) + "字节");
            items.SubItems.Add(listView1.Items[index].SubItems[1].Text);
            listView2.Items.Add(items);
            listView2.Items[listView2.Items.Count - 1].Selected = true;
            listView2.Focus();
        }
        //将listview2中的项移除
        private void btn_removelist_Click(object sender, EventArgs e) {
            if (listView2.SelectedItems.Count <= 0) {
                MessageBox.Show("没有选中项");
                return;
            }
            int index = listView2.SelectedItems[0].Index;
            listView2.Items.RemoveAt(index);
            if (listView2.Items.Count <= 0)
                return;
            if (index >= 1 && index >= listView2.Items.Count) {
                index--;
            }
            listView2.Items[index].Selected = true;
            listView2.Focus();
        }
        //重置窗体界面
        public void ReSetForm() {
            groupBox1.Enabled = true;
            groupBox2.Enabled = false;
            btn_next.Enabled = false;
            comboBox1.Items.Remove("未变动数据");
            comboBox1.Items.Remove("变动的数据");
            comboBox1.SelectedIndex = 0;
            btn_first.Text = "首次搜索";
            label1.Text = "等待搜索...";
            comboBox2.Enabled = true;
            btn_next.Enabled = false;
            arrLastSearchAddrs = null;
            arrLastSearchValues = null;
            listTempAddrs.Clear();
            listTempValues.Clear();
            listView1.Items.Clear();
            panel1.Hide();
            GC.Collect();
        }
        //禁用与恢复控件
        public void EnableControls(bool bState) {
            btn_open.Enabled =
                groupBox1.Enabled =
                listView1.Enabled =
                listView2.Enabled = bState;
            groupBox2.Enabled = false;
        }
        //取消修改描述信息
        private void btn_display_cancel_Click(object sender, EventArgs e) {
            panel1.Hide();
        }
        //确认修改描述信息
        private void btn_display_ok_Click(object sender, EventArgs e) {
            listView2.Items[nSelectIndex].SubItems[0].Text = tbx_displayinfo.Text;
            panel1.Hide();
        }

        private void 设置描述信息ToolStripMenuItem_Click(object sender, EventArgs e) {
            if (listView2.Items.Count <= 0)
                return;
            panel1.Location = listView2.Location;
            panel1.Show();
            nSelectIndex = listView2.SelectedItems[0].Index;
            tbx_displayinfo.Text = listView2.Items[nSelectIndex].SubItems[0].Text;
            tbx_displayinfo.Focus();
        }

        private void 复制ToolStripMenuItem_Click(object sender, EventArgs e) {
            if (listView2.SelectedItems.Count <= 0)
                return;
            nSelectIndex = listView2.SelectedItems[0].Index;
            string strTemp = listView2.Items[nSelectIndex].SubItems[0].Text
                + "\t" + listView2.Items[nSelectIndex].SubItems[1].Text
                + "\t" + listView2.Items[nSelectIndex].SubItems[2].Text
                + "\t" + listView2.Items[nSelectIndex].SubItems[3].Text;
            Clipboard.SetText(strTemp);
        }

        private void 复制全部ToolStripMenuItem_Click(object sender, EventArgs e) {
            if (listView2.Items.Count <= 0)
                return;
            string strTemp = "";
            for (int i = 0, loopLen = listView2.Items.Count; i < loopLen; i++) {
                strTemp += listView2.Items[i].SubItems[0].Text
                    + "\t" + listView2.Items[i].SubItems[1].Text
                    + "\t" + listView2.Items[i].SubItems[2].Text
                    + "\t" + listView2.Items[i].SubItems[3].Text + "\r\n";
            }
            Clipboard.SetText(strTemp);
        }

        private void 全部移除ToolStripMenuItem_Click(object sender, EventArgs e) {
            listView1.Items.Clear();
        }
    }
}
